package com.example.pan.aria2

import com.example.pan.ext.toJson
import com.example.pan.http.ApiService
import com.example.pan.model.DownloadTaskInfo
import com.example.pan.ui.formatBit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject

class Aria2Repository @Inject constructor(
    private val service: ApiService,
) {

    private val cache: MutableMap<String, DownloadTaskInfo> = ConcurrentHashMap()

    @ExperimentalCoroutinesApi
    fun addUri(url: String, fileName: String) = flow {
        val option = HashMap<String, Any>()
        option["out"] = fileName
        val response = service.addUri(
            requestBody(
                "aria2.addUri", arrayListOf(
                    arrayListOf(url),
                    option
                )
            )
        )
        val gid = response.result
        cache[gid] = DownloadTaskInfo(gid, fileName)
        emit(gid)
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun tellAllTask() = flow {
        while (true) {
            val map = arrayListOf(
                service.tellActive(requestBody("aria2.tellActive", arrayListOf())),
                service.tellWaiting(requestBody("aria2.tellWaiting", arrayListOf(-1, 1000))),
                service.tellStopped(requestBody("aria2.tellStopped", arrayListOf(-1, 1000))),
            ).flatMap {
                it.result
            }.mapNotNull {
                cache[it.gid]?.let { item ->
                    val tmp = DownloadTaskInfo(item.gid, item.fileName)
                    tmp.connections = it.connections
                    tmp.totalLength = formatBit(it.totalLength)
                    tmp.completeLength = formatBit(it.completedLength)
                    tmp.downloadSpeed = formatBit(it.downloadSpeed) + "/s"
                    tmp.status = it.status
                    tmp.process =
                        (it.completedLength.toDouble() / it.totalLength.toDouble() * 100).toInt()
                    it.errorMessage.apply {
                        tmp.errorMsg = this
                    }
                    tmp
                }
            }
            emit(map)
            delay(1000)
        }
    }.flowOn(Dispatchers.IO)

    suspend fun allTask(): List<DownloadTaskInfo> {


        return emptyList()
    }

    @ExperimentalCoroutinesApi
    fun getVersion() = flow {
        val response = service.getVersion(requestBody("aria2.getVersion", arrayListOf()))
        emit(response.result)
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun getGlobalOption() = flow {
        val response =
            service.getGlobalOption(requestBody("aria2.getGlobalOption", arrayListOf()))
        emit(response.result)
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun pause(gid: String) = flow {
        val response =
            service.pause(requestBody("aria2.forcePause", arrayListOf(gid)))
        emit(response.result)
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun unPause(gid: String) = flow {
        val response =
            service.pause(requestBody("aria2.unpause", arrayListOf(gid)))
        emit(response.result)
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun remove(gid: String) = flow {
        val response =
            service.pause(requestBody("aria2.forceRemove", arrayListOf(gid)))
        emit(response.result)
    }.flowOn(Dispatchers.IO)


    private fun requestBody(method: String, params: ArrayList<Any>): RequestBody {
        val map = HashMap<String, Any>()
        map["jsonrpc"] = "2.0"
        map["method"] = method
        map["id"] = "QXJpYU5nXzE2MTM4OTMxMjBfMC4wODg4ODM0MTIzNDc0Mzc4"
        map["params"] = params
        return map.toJson().toRequestBody()
    }

}